package dkbta.ontology.loading;

import static dkbta.ontology.elements.abstractions.gradient.GradientMappingFunction.NECESSARY_VALUES;
import static dkbta.util.Utils.enumForName;

import java.util.Arrays;
import java.util.EnumSet;
import java.util.Set;

import org.w3c.dom.Element;

import dkbta.ontology.Ontology;
import dkbta.ontology.elements.ElementType;
import dkbta.ontology.elements.TypedName;
import dkbta.ontology.elements.abstractions.TemporalSemanticProperty;
import dkbta.ontology.elements.abstractions.gradient.GradientDef;
import dkbta.ontology.elements.abstractions.gradient.GradientMappingFunction;
import dkbta.ontology.elements.abstractions.interpolation.PersistenceFunction;
import dkbta.ontology.values.ScaledSymbolicValues;
import dkbta.ontology.values.SymbolicValues;
import dkbta.ontology.values.Values;
import dkbta.ontology.values.ValuesType;
import dkbta.util.time.TimeUnit;

public class GradientParser extends AbstractionParser{

	public GradientParser(Ontology ontology){
		super(ontology, EnumSet.complementOf(EnumSet.of(ElementType.PRIMITIVE)));
	}

	@Override
	public ElementType getElementType(){
		return ElementType.GRADIENT;
	}

	@Override
	protected void parseAbstraction(Element gradient, String name, String componentType,
		Set<TypedName> abstractedFrom, Set<String> necessaryContexts,
		Set<TemporalSemanticProperty> tsps, ScaledSymbolicValues scaledValues,
		SymbolicValues values, PersistenceFunction persistFunc) throws OntologyException{
		if (abstractedFrom.size() != 1){
			throw new OntologyException(
					"A gradient can be abstracted from only one primitive");
		}
		String afPrimitive = abstractedFrom.iterator().next().getName();

		for (String value : NECESSARY_VALUES){
			if (!values.isValid(ValuesType.SYMBOLIC, value)){
				throw new OntologyException("The values for the gradient '" + values
						+ "' do not contain all necessary values: "
						+ Arrays.toString(NECESSARY_VALUES));
			}
		}

		GradientMappingFunction mapFunc = parseMappingFunction(gradient, values,
			abstractedFrom);
		if (mapFunc == null){
			throw new OntologyException("Gradient without a mapping function");
		}
		_ontology.addGradient(new GradientDef(name, componentType, scaledValues,
				afPrimitive, necessaryContexts, tsps, mapFunc, persistFunc));
	}

	private GradientMappingFunction parseMappingFunction(Element gradient, Values values,
		Set<TypedName> abstractedFrom) throws OntologyException{
		Element mappingFuncElement = OntologyParser.getXmlElement(gradient,
			"MappingFunction");
		if (mappingFuncElement == null){
			return null;
		}

		String significantVariationStr = mappingFuncElement
				.getAttribute("significantVariation");
		double significantVariation = -1;
		try{
			significantVariation = Double.valueOf(significantVariationStr);
		}catch(NumberFormatException e){}
		if (significantVariation < 0){
			throw new OntologyException(
					"Missing/invalid significantVariation attribute in the "
							+ "gradient's mapping function: '" + significantVariationStr
							+ "'. Must be a positive number.");
		}
		TimeUnit timeUnit = enumForName(TimeUnit.class, mappingFuncElement
				.getAttribute("timeUnit"));

		return new GradientMappingFunction(significantVariation, timeUnit);
	}
}
